💡 AI 인사이트

🤖 AI가 여기에 결과를 출력합니다...

댓글 커뮤니티

쿠팡이벤트

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

검색

    로딩 중이에요... 🐣

    [코담] 웹개발·실전 프로젝트·AI까지, 파이썬·장고의 모든것을 담아낸 강의와 개발 노트

    1 requests + BeautifulSoup 방식의 크롤링 | ✅ 저자: 이유정(박사)

    https://www.crummy.com/software/BeautifulSoup/bs4/doc.ko/

    사이트주소 http://www.cgv.co.kr/movies/?lt=1&ft=0

    BeautifulSoup & HTML 파서

    pip install beautifulsoup4
    pip install beautifulsoup4 lxml
    
    pip list # 설치확인
    

    BeautifulSoup은 HTML 파서를 직접 제공하지 않기 때문에 lxml 또는 html5lib 중 하나도 함께 설치하는 것이 좋습니다

    # requests와 BeautifulSoup 라이브러리를 불러온다. (웹페이지 요청 및 HTML 파싱용)
    import requests
    from bs4 import BeautifulSoup
    
    # CGV 영화 정보를 수집하는 함수 정의
    def get_cgv_movies():
        # CGV 영화 페이지 URL을 변수에 저장
        url = "http://www.cgv.co.kr/movies/"
    
        # 요청할 때 사용하는 헤더(User-Agent)는 브라우저처럼 보이게 하기 위해 설정
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
        }
    
        # 웹페이지에 GET 요청을 보내고 응답을 받아온다
        resp = requests.get(url, headers=headers)
    
        # 요청이 실패하면 예외를 발생시켜 프로그램을 중단시킨다
        resp.raise_for_status()
    
        # 받은 HTML 문서를 BeautifulSoup을 이용해 파싱한다 
        # (html.parser는 파서 종류)
        soup = BeautifulSoup(resp.text, "html.parser")
    
        # 영화 목록이 들어 있는 태그(ol > li)를 모두 선택하여 리스트로 저장
        chart_box = soup.select("div.sect-movie-chart ol li")
    

    왜 이런 순서로 해야 할까?

    • 먼저 웹페이지에 접속해야 응답이 생기고
    • 응답이 정상인지 확인하고
    • 그 응답 안의 HTML을 파싱하고
    • 그 HTML 안에서 필요한 정보를 선택합니다.

    비유로 정리하면: 택배 상자 받기 → 상자에 문제가 없는지 확인 → 상자 열기 → 안에서 정보 찾기

    # 결과를 담을 리스트를 미리 만들어 놓는다
        result = []
    
        # 영화 하나하나씩 반복하면서 정보를 추출한다
        for movie in chart_box:
        
            # 영화 제목이 들어 있는 <strong class="title"> 태그를 찾는다
            title_tag = movie.select_one("strong.title")
            
            # 예매율이 들어 있는 <strong class="percent"> 태그 내부의 <span>을 찾는다
            percent_tag = movie.select_one("strong.percent span")
            
            # 개봉일 정보가 들어 있는 <span class="txt-info"> 태그를 찾는다
            release_tag = movie.select_one("span.txt-info")
            
            # 영화 포스터 이미지를 나타내는 <img> 태그를 찾는다
            img_tag = movie.select_one("img")
    

    태그 분석의 주요 기준: 태그 이름: <div>, <span>, <a> 등 ( 예시:soup.find('div'))

    클래스명(class): 대부분의 사이트가 디자인/구조 분리를 위해 많이 사용 ( 예시: soup.select('.title'))

    id 속성: 페이지에서 유일함 (단 하나의 요소 찾기에 적합) ( 예시:soup.select('#main'))

    태그의 위치(계층 구조): 부모-자식 구조를 통해 위치 기반으로 탐색 ( 예시:div.container > ul > li)

    속성(attribute): href, src, data-*, title 등 ( 예시:soup.find('a', href=True))

    텍스트 내용: 텍스트 자체를 기준으로 필터링 ( 예시:if "CGV" in tag.text:)

    메서드 정리: find(): 태그 기반 (한 개) find_all(): 태그 기반 (여러 개) select(): CSS 선택자 방식 (여러 개) select_one(): CSS 선택자 방식 (한 개)

    find() 사용 시 (속성 기반)

    soup.find("div", class_="title")      # class명
    soup.find("div", id="main")           # id명
    

    select() 사용 시 (CSS 선택자 방식)

    soup.select("div.title")              # class명
    soup.select("div#main")               # id명
    soup.select("div.box > ul > li")      # 위치 기반 선택
    
    percent_tag = movie.select_one("strong.percent span")
    

    해석: 이 구조에서 span을 가져오려는 겁니다.

    <strong class="percent">
        <span>예매율 52%</span>
    </strong>
    

    조건부 파싱 + 딕셔너리 구성을 동시에 처리

            # 제목이 있는 경우에만 정보를 result 리스트에 추가한다
            if title_tag:
                result.append({
                    "title": title_tag.text.strip(),  
                    # 제목 텍스트에서 양쪽 공백 제거 후 저장
                    
                    "reserve_percent": percent_tag.text.strip() if percent_tag else None,  
                    # 예매율이 있다면 텍스트 저장, 없으면 None
                    
                    "release_date": release_tag.text.strip() if release_tag else None,  
                    # 개봉일이 있다면 텍스트 저장, 없으면 None
                    
                    "poster": img_tag["src"] if img_tag else None  
                    # 포스터 이미지의 src 속성값을 저장
                })
        
        # 최종적으로 영화 정보를 담은 리스트를 반환한다
        return result
    
    • title_tag에서 텍스트만 뽑고 양쪽 공백 제거 (strip())

    • 예: <strong class="title"> 쥬라기 월드 </strong>"쥬라기 월드"

    • 개봉일 정보를 가진 release_tag가 있으면 텍스트 추출 없으면 None (정보가 누락된 경우 대비)

    • 포스터 이미지를 담고 있는 <img> 태그가 있다면 → src 속성값만 가져옴 (이미지 주소) 없으면 None

    • 예: <img src="https://...jpg">"https://...jpg"


    # 이 파일이 메인으로 실행될 때만 아래 코드가 실행되도록 설정
    if __name__ == "__main__":
        # 위에서 정의한 함수 실행 → 영화 정보 리스트 가져오기
        movies = get_cgv_movies()
    
        # 영화 리스트를 반복문으로 출력 (번호와 함께 출력)
        for i, m in enumerate(movies, 1): # 인덱스 1부터  
        # enumerate는 번호(1부터)와 데이터를 함께 반환
        
            print(f"{i}. {m['title']} ({m['release_date']}) - 예매율: {m['reserve_percent']}")
    

    예상 출력결과:

    1. 쥬라기 월드 (2025.07.04) - 예매율: 52.3%
    
    TOP
    preload preload